#include "ExampleAIModule.h"
#include "StarUtil.h"
using namespace BWAPI;
using namespace std;

bool analyzed;
bool analysis_just_finished;
BWTA::Region* home;
BWTA::Region* enemy_base;
StrategyGen strat;
Alphabetical alphaStrat;
Alphabetical strat2;
CompStrat compStrat;
BattleSimulator* completed;
BattleSimulator* nextSimulation;
StratChooser chooser;
int simulationCount;
void ExampleAIModule::onStart()
{
 	Broodwar->printf("The map is %s, a %d player map",
		Broodwar->mapName().c_str(),Broodwar->getStartLocations().size());
	// Enable some cheat flags
    Broodwar->enableFlag(Flag::UserInput);
    // Uncomment to enable complete map information
    Broodwar->enableFlag(Flag::CompleteMapInformation);

    //read map information into BWTA so terrain analysis can be done in 
	//another thread
    BWTA::readMap();
    analyzed=false;
    analysis_just_finished=false;

    show_bullets=false;
    show_visibility_data=false;

    if (Broodwar->isReplay())
    {
        Broodwar->printf("The following players are in this replay:");
        for(std::set<Player*>::iterator p=Broodwar->getPlayers().begin();
			p!=Broodwar->getPlayers().end();p++)
        {
            if (!(*p)->getUnits().empty() && !(*p)->isNeutral())
            {
                Broodwar->printf("%s, playing as a %s",(*p)->getName().c_str(),
					(*p)->getRace().getName().c_str());
            }
        }
    }
    else
    {
        Broodwar->printf("The match up is %s v %s",
        Broodwar->self()->getRace().getName().c_str(),
        Broodwar->enemy()->getRace().getName().c_str());
    }
	//assignTargets();
}

void ExampleAIModule::onEnd(bool isWinner)
{
  if (isWinner)
  {
    //log win to file
  }
  StarUtil::deleteValues();
}




void ExampleAIModule::onFrame()
{
	static int frameCount = 0;
	if (Broodwar->isReplay()){
		return;

	}
	
	static bool has_detected = false;
	if(!has_detected){//First frame inisalizes simulation
	
		has_detected = true;
		if(SIMULATE_CHOOSER)
		{
			chooser.setup();
			simulateChoose();
			strat.setTargets(StarUtil::getEnemyTypes());
		}
		else
		{
			alphaStrat.setTargets(StarUtil::getEnemyTypes());
			strat.setTargets(StarUtil::getEnemyTypes());
		}
		strat2.setTargets(StarUtil::getPlayerTypes());
		compStrat.setTargets(StarUtil::getPlayerTypes());
		assignTargets();
		if(FAST_BATTLES)
			Broodwar->setLocalSpeed(0);
	}

	if(SIMULATE_TEST && frameCount)
	{
		simulateBattle();
	}
	
	Player * player = Broodwar->self();
	drawStats(Broodwar->self(), 5,"AI");
	drawStats(StarUtil::getEnemy(),125, "Computer");
	if(SIMULATE_CHOOSER)
		drawStrat();
	frameCount++;
}

void ExampleAIModule::onUnitDestroy(BWAPI::Unit* unit)
{
	if (!Broodwar->isReplay() && Broodwar->getFrameCount()>1)
    {

	    assignTargets();
    }
}

DWORD WINAPI AnalyzeThread()
{
    BWTA::analyze();

    //self start location only available if the map has base locations
    if (BWTA::getStartLocation(BWAPI::Broodwar->self())!=NULL)
    {
        home = BWTA::getStartLocation(BWAPI::Broodwar->self())->getRegion();
    }
    //enemy start location only available if Complete Map Information is enabled.
    if (BWTA::getStartLocation(BWAPI::Broodwar->enemy())!=NULL)
    {
       enemy_base= BWTA::getStartLocation(BWAPI::Broodwar->enemy())->getRegion();
    }
    analyzed   = true;
    analysis_just_finished = true;
    return 0;
}

//Draws stats for the player given starting at the location give.
int ExampleAIModule::drawStats(Player * player, int start, string playerName)
{

    map<string, vector<Unit*>*> unitGroups;
	if(player->getID() == Broodwar->self()->getID()){
		unitGroups = StarUtil::getPlayerGroups();
	}else{
		unitGroups = StarUtil::getEnemyGroups();
	}
	set<Unit*> myUnits = player->getUnits();
    Broodwar->drawTextScreen(start,0,"%s %d units:",playerName.c_str(),
		myUnits.size());
    std::map<string, int> unitTypeCounts;
    for(std::map<string, vector<Unit*>*>::iterator i=unitGroups.begin();
		i!=unitGroups.end();i++)
    {
		int count = -1;
		if((*i).second != NULL){
			count = (*i).second->size();
		}
		unitTypeCounts[(*i).first] = count;
	}
	int line=1;
	for(std::map<string,int>::iterator i=unitTypeCounts.begin();
		i!=unitTypeCounts.end();i++)
	{
		Broodwar->drawTextScreen(start,16*line,"- %d %ss",
			(*i).second, (*i).first.c_str());
		line++;
	}
	return myUnits.size();
}

void ExampleAIModule::drawStrat()
{
	vector<string> units = StarUtil::getSelfUnitTypes();
	vector<string> unitStrat;
	int start = 10, line = 66;
	Broodwar->drawTextScreen(start,line,"TARGETING");
	for(vector<string>::iterator first = units.begin();
		first != units.end(); first++)
	{
		line += 16;
		start = 10;
		unitStrat = strat.getTargetPriority(*first);
		Broodwar->drawTextScreen(start,line,"%s:",(*first).c_str());
		for(vector<string>::iterator sec= unitStrat.begin();
			sec != unitStrat.end(); sec++)
		{
			start += 100;
			Broodwar->drawTextScreen(start,line,"%s",(*sec).c_str());
		}

	}
}
void ExampleAIModule::doStrat()
{

}

void ExampleAIModule::drawBullets()
{
  std::set<Bullet*> bullets = Broodwar->getBullets();
  for(std::set<Bullet*>::iterator i=bullets.begin();i!=bullets.end();i++)
  {
    Position p=(*i)->getPosition();
    double velocityX = (*i)->getVelocityX();
    double velocityY = (*i)->getVelocityY();
    if ((*i)->getPlayer()==Broodwar->self())
    {
      Broodwar->drawLineMap(p.x(),p.y(),
		  p.x()+(int)velocityX,p.y()+(int)velocityY,Colors::Green);
      Broodwar->drawTextMap(p.x(),p.y(),"\x07%s",
		  (*i)->getType().getName().c_str());
    }
    else
    {
      Broodwar->drawLineMap(p.x(),p.y(),
		  p.x()+(int)velocityX,p.y()+(int)velocityY,Colors::Red);
      Broodwar->drawTextMap(p.x(),p.y(),
		  "\x06%s",(*i)->getType().getName().c_str());
    }
  }
}

void ExampleAIModule::drawVisibilityData()
{
  for(int x=0;x<Broodwar->mapWidth();x++)
  {
    for(int y=0;y<Broodwar->mapHeight();y++)
    {
      if (Broodwar->isExplored(x,y))
      {
        if (Broodwar->isVisible(x,y))
          Broodwar->drawDotMap(x*32+16,y*32+16,Colors::Green);
        else
          Broodwar->drawDotMap(x*32+16,y*32+16,Colors::Blue);
      }
      else
        Broodwar->drawDotMap(x*32+16,y*32+16,Colors::Red);
    }
  }
}

void ExampleAIModule::specialAttack(BWAPI::Unit *attacker, BWAPI::Unit * target)
{
   // get the unit's current command
   BWAPI::UnitCommand currentCommand(attacker->getLastCommand());
  // if we've already told this unit to attack this target, ignore this command
   if (currentCommand.getType() == BWAPI::UnitCommandTypes::Attack_Unit
		&&   currentCommand.getTarget() == target)
       {
               return;
       }
       // if nothing prevents it, attack the target
       attacker->attack(target);
}

void ExampleAIModule::assignTargets()
{
	//group enemies by type
	map<string, vector<Unit*>*> enemyGroups = StarUtil::getEnemyGroups();
	
	
	//order units to attack closest of highest priority
	set<Unit*> myUnits = Broodwar->self()->getUnits();
	map<string, Unit*> focus;

	for(set<Unit*>::iterator mine = myUnits.begin();
		mine != myUnits.end(); mine++)
	{

		
		string name = 	(*mine)->getType().getName();
		if(focus[name] != NULL)
		{
			specialAttack(*mine,focus[name]);
		}
		//get priority for this unit
		string myType = (*mine)->getType().getName();
		
		vector<string> priority;
		if(SIMULATE_TEST)
			priority = alphaStrat.getTargetPriority(myType);
		else
			priority = strat.getTargetPriority(myType);
		for(vector<string>::iterator groups = priority.begin();
			groups != priority.end(); groups++)
		{
			vector<Unit*>* group = enemyGroups[*groups];
			if(group == NULL || group->empty())
			{
				//Broodwar->printf("Enemy group %s is at %d and may be empty", 
				//				(*groups).c_str(), group);
				//continue to next enemey group
				continue;
			}
				
			double dis = (*mine)->getDistance(group->front());
			Unit* closest = group->front();
			if(!StarUtil::unitCanAttack((*mine)->getType().getName(), 
				closest->getType().getName()))
			{
				continue;
			}
			//get closest in group
			for(vector<Unit*>::iterator target = ++group->begin();
				target != group->end(); target++)
			{
				double newDis = (*mine)->getDistance(*target);
				if(newDis < dis){
					closest = *target;
					dis = newDis;
				}
			}
			specialAttack(*mine,closest);
			if(!(StarUtil::isMelee((*mine)->getType())))
				focus[name] = closest;
			break;
			
		}
	}
	

}

void ExampleAIModule::simulateBattle()
{
	if(nextSimulation == NULL){
		nextSimulation = new BattleSimulator(&strat, &compStrat);
	}

	int counter = 0;

	while(!nextSimulation->isDone() && counter < 20){	
		
		nextSimulation->step();
		counter++;
	}

	if(nextSimulation->isDone()){
		if(completed != NULL){
			delete completed;
		}
		completed = nextSimulation;
		nextSimulation = NULL;
		simulationCount++;
	}

	drawSimulation();
}

void ExampleAIModule::simulateChoose()
{
	int counter = 0;
	while(chooser.isNotDone() && counter < 50)
	{	
		chooser.resolve();
		counter++;
	}
	if(!chooser.isNotDone())
	{
		strat = chooser.getBestStrat();
		assignTargets();
	}
	

}


void ExampleAIModule::drawSimulation(){

	
	
	if(completed == NULL){
		return;
	}

	map<string, vector<BattleSimulator::UnitStats*>*>* friendlyUnits  = 
									completed->getRemainingUnits(true);
	map<string, vector<BattleSimulator::UnitStats*>*>* enemyUnits  = 
									completed->getRemainingUnits(false);
	int startY = 150;
	int friendlyX = 50;
	int friendlyY = startY;
	int enemyX = 250;
	int enemyY = startY;
	int spacing = 16;
	
	

	Broodwar->drawTextScreen(100, 130, "Simulation Count : %d", simulationCount);
	Broodwar->drawTextScreen(friendlyX, friendlyY, "Remaining friendly Units");
	Broodwar->drawTextScreen(enemyX, enemyY, "Remaining enemy Units");
	
	friendlyY += spacing;
	enemyY += spacing;

	for(map<string, vector<BattleSimulator::UnitStats*>*>::iterator type 
		= friendlyUnits->begin(); type != friendlyUnits->end(); type++)
	{
		int count = 0;
		for(vector<BattleSimulator::UnitStats*>::iterator unit =
			(*type).second->begin();unit != (*type).second->end();unit++)
		{
			if(!(*unit)->dead)
				count++;
		}
		
		
		Broodwar->drawTextScreen(friendlyX,friendlyY,
								 "- %d %ss",
								 count, 
								 (*type).first.c_str());
		friendlyY += spacing;
	}

	for(map<string, vector<BattleSimulator::UnitStats*>*>::iterator type 
		= enemyUnits->begin(); type != enemyUnits->end(); type++)
	{
		int count = 0;
		for(vector<BattleSimulator::UnitStats*>::iterator unit = 
			(*type).second->begin();	unit != (*type).second->end();unit++)
		{
			if(!(*unit)->dead)
				count++;
		}
		Broodwar->drawTextScreen(enemyX,enemyY,
								 "- %d %ss",
								 count, 
								 (*type).first.c_str());
		enemyY += spacing;
	}

}


